﻿using System;
using System.Collections.Generic;
using System.Text;

namespace eKanBan.Model
{
    [Serializable]
    class BasePhase : IEnumerable<Feature>, IEnumerable<BasePhase>
    {
        private List<BasePhase> nextPhases;
        private List<Feature> features;

        
        public int NextPhaseCount { get { return nextPhases.Count; } }
        public int FeatureCount { get { return features.Count; } }
        public IList<BasePhase> NextPhases { get { return nextPhases.AsReadOnly(); } }
        public IList<Feature> Features { get { return features.AsReadOnly(); } }


        public override string ToString()
        {
            string r = String.Format("Name: {0}\n", Name);
            r += "Next Phases: \n";
            foreach (BasePhase phase in nextPhases)
                r += String.Format("\t{0}\n", phase.Name);
            r += "Features: \n";
            foreach (Feature feature in features)
                r += String.Format("\t{0}\n", feature.Description);

            return r;
        }

        public BasePhase()
        {
            nextPhases = new List<BasePhase>();
            features = new List<Feature>();
        }

        public BasePhase(string name)
            : this()
        {
            this.Name = name;
        }

        // Add a feature to this phase
        public void Add(Feature feature)
        {
            if (!CanAdd(feature))
                throw new Exception("Cannot add feature to current phase");

            features.Add(feature);
        }

        // Remove a feature from this phase
        public void Remove(Feature feature)
        {
            if (!CanRemove(feature))
                throw new Exception("Cannot remove feature from current phase");

            features.Remove(feature);
        }

        // Check the condition whether the feature can be added to this phase or not
        public virtual bool CanAdd(Feature feature)
        {
            return (!features.Contains(feature));
        }

        // Check the condition whether the feature can be removed from this phase or not
        public bool CanRemove(Feature feature)
        {
            return (features.Contains(feature));
        }

        // property, name of this phase
        public string Name { get; set; }

        // can move directly to nextPhase or not
        public bool CanMoveDirect(BasePhase nextPhase)
        {
            return (nextPhases.Contains(nextPhase));
        }

        // can move to nextPhase or not (directly or indirectly)
        public bool CanMove(BasePhase nextPhase)
        {
            Queue<BasePhase> q = new Queue<BasePhase>();
            q.Enqueue(this);
            while (q.Count > 0)
            {
                BasePhase curPhase = q.Dequeue();
                foreach (BasePhase c in curPhase.nextPhases)
                {
                    if (c.Equals(nextPhase))
                        return true;

                    q.Enqueue(c);
                }
            }

            return false;
        }

        // add directy next phase to this phase 
        // throw error if can move from next phase to this phase (make cycle)
        public void AddNextPhase(BasePhase phase)
        {
            if (phase == this || phase.CanMove(this))
                throw new Exception("Cannot add next phase. Cycle detected.");

            nextPhases.Add(phase);
        }

        public void RemoveNextPhase(BasePhase phase)
        {
            nextPhases.Remove(phase);
        }

        public IEnumerator<Feature> GetEnumerator()
        {
            return features.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }

        IEnumerator<BasePhase> IEnumerable<BasePhase>.GetEnumerator()
        {
            return nextPhases.GetEnumerator();
        }
    }
}
